home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / network / nsclilib / ni_encr.c < prev    next >
Text File  |  1996-07-05  |  30KB  |  998 lines

  1. /*
  2. * ===========================================================================
  3. *
  4. *                            PUBLIC DOMAIN NOTICE
  5. *               National Center for Biotechnology Information
  6. *
  7. *  This software/database is a "United States Government Work" under the
  8. *  terms of the United States Copyright Act.  It was written as part of
  9. *  the author's official duties as a United States Government employee and
  10. *  thus cannot be copyrighted.  This software/database is freely available
  11. *  to the public for use. The National Library of Medicine and the U.S.
  12. *  Government have not placed any restriction on its use or reproduction.
  13. *
  14. *  Although all reasonable efforts have been taken to ensure the accuracy
  15. *  and reliability of the software and data, the NLM and the U.S.
  16. *  Government do not and cannot warrant the performance or results that
  17. *  may be obtained by using this software or data. The NLM and the U.S.
  18. *  Government disclaim all warranties, express or implied, including
  19. *  warranties of performance, merchantability or fitness for any particular
  20. *  purpose.
  21. *
  22. *  Please cite the author in any work or product based on this material.
  23. *
  24. * ===========================================================================
  25. *
  26. * File Name:    ni_encr.c
  27. *
  28. * Author:       Epstein
  29. *
  30. * Version Creation Date:        2/14/94
  31. *
  32. * $Revision: 4.0 $
  33. *
  34. * File Description:
  35. *   Supports RSAREF-based encryption for NCBI Network Services client-server
  36. *   architecture
  37. *
  38. *
  39. * Modifications:
  40. * --------------------------------------------------------------------------
  41. * Date     Name        Description of modification
  42. * -------  ----------  -----------------------------------------------------
  43. * 02/22/94 Epstein     Fix reading of public keys from config. file, and
  44. *                      initialization of random data structure for RSA
  45. *                      encryption.
  46. * 03/04/94 Epstein     Reduce memory leakage, avoid ErrPost error if unable
  47. *                      to open public-key file.
  48. * 03/09/94 Epstein     Add length parameter to NI_LoadPrivKey(), add code
  49. *                      which allows {Transient}SetAppParam() to provide
  50. *                      some help in providing an unbreakable key.  Also
  51. *                      use high-granularity timing in generating random
  52. *                      key, where available.
  53. * 07/14/94 Epstein     Pad buffer more efficiently in DoDesWrite().
  54. *
  55. *
  56. * RCS Modification History:
  57. * $Log: ni_encr.c,v $
  58.  * Revision 4.0  1995/07/26  13:56:32  ostell
  59.  * force revision to 4.0
  60.  *
  61.  * Revision 1.8  1995/05/17  17:52:00  epstein
  62.  * add RCS log revision history
  63.  *
  64. */
  65.  
  66. #include <ncbi.h>
  67. #include <ni_types.h>
  68. #include <global.h>
  69. #include <des.h>
  70. #include <rsaref.h>
  71. #ifdef OS_UNIX
  72. #include <sys/time.h>
  73. #endif /* OS_UNIX */
  74.  
  75. #define ENCR_DES_TYPE                 1
  76.  
  77. #define ENCR_DES_STATE_IDLE           0
  78. #define ENCR_DES_STATE_GOT1LENBYTE    1
  79. #define ENCR_DES_STATE_INSTREAM       2
  80.  
  81.  
  82.  
  83.  
  84. /*
  85.  * Purpose:     Encrypts a buffer using DES
  86.  *
  87.  * Parameters:
  88.  *   encr       Encryption data structure, includes Cypher-block-chaining info
  89.  *   buf        Input buffer
  90.  *   len        Length of buf
  91.  *   tmpbuf     Output buffer
  92.  *
  93.  * Returns:
  94.  *                The length of the resulting encrypted data
  95.  *
  96.  * Description:
  97.  *              Encodes the specified input buffer using two bytes which contain
  98.  *              the length of the _plaintext_ data which follows, followed by
  99.  *              the encrypted text.  Thus, for example, an input buffer of
  100.  *              length 9 will result in 0x0, 0x9, followed by 16 bytes of
  101.  *              encrypted data.
  102.  *
  103.  * Note:
  104.  *              For safety, the output buffer must be large enough to accomodate
  105.  *              the two-byte header, plus the length of the input buffer, plus
  106.  *              an additional seven bytes.
  107.  *
  108.  *              DES cipher-block chaining (CBC) is used, and the CBC information
  109.  *              is encoded in the desWriteContext data structure.
  110.  */
  111.  
  112. static Int4
  113. DoDesWrite(NI_EncrDataPtr encr, CharPtr buf, int len, CharPtr tmpbuf)
  114. {
  115.     int encrLen;
  116.     UcharPtr encrBuf;
  117.     UcharPtr outbuf = (UcharPtr) tmpbuf;
  118.  
  119.     outbuf[0] = len / 256; /* high order byte */
  120.     outbuf[1] = len % 256; /* low order byte */
  121.  
  122.     encrLen = ((len + 7) / 8) * 8;
  123.     encrBuf = (UcharPtr) MemNew(encrLen);
  124.  
  125.     /* pad with zeros */
  126.     MemSet ((CharPtr) &encrBuf[len], '\0', encrLen - len);
  127.  
  128.     MemCpy ((CharPtr) encrBuf, buf, len);
  129.     DES_CBCUpdate ((DES_CBC_CTX PNTR) encr->desWriteContext, &outbuf[2],
  130.                    encrBuf, encrLen);
  131.     MemFree (encrBuf);
  132.     return (encrLen + 2);
  133. }
  134.  
  135.  
  136. /*
  137.  * Purpose:     Encryption write-filter for DES
  138.  *
  139.  * Parameters:
  140.  *   mhvoid     Pointer to message handle data structure
  141.  *   buf        Input buffer
  142.  *   len        Length of buf
  143.  *   tmpbuf     Output buffer
  144.  *
  145.  * Returns:
  146.  *                The length of the resulting encrypted data
  147.  *
  148.  * Description:
  149.  *              Validates the input and then processes the data using
  150.  *              DoDesWrite().
  151.  */
  152.  
  153. static Int4
  154. DesWriteFilt(VoidPtr mhvoid, CharPtr buf, int len, CharPtr tmpbuf)
  155. {
  156.     NI_HandPtr mh = (NI_HandPtr) mhvoid;
  157.  
  158.     if (len <= 0 || tmpbuf == NULL || buf == NULL || mh == NULL ||
  159.         mh->encryption == NULL)
  160.         return 0;
  161.     return DoDesWrite(mh->encryption, buf, len, tmpbuf);
  162. }
  163.  
  164.  
  165. /*
  166.  * Purpose:     Decrypts a buffer using DES
  167.  *
  168.  * Parameters:
  169.  *   encr       Encryption data structure, includes Cypher-block-chaining info
  170.  *   buf        Input buffer containing encrypted data, and output plaintext
  171.  *   bytesRead  Length of input data in buf
  172.  *   len        Max # of bytes which will fit in buf (requested read length)
  173.  *
  174.  * Returns:
  175.  *                The length of the resulting plaintext which has been processed
  176.  *
  177.  * Description:
  178.  *              Decodes the specified input buffer, using a protocol which
  179.  *              consists of two bytes of length information (realDataLeft)
  180.  *              followed by bytesToRead bytes of encrypted data, where bytesToRead
  181.  *              is realDataLeft padded out to a multiple of eight bytes.
  182.  *
  183.  *              The algorithm uses a state machine to process as much of the input
  184.  *              buffer as possible, and to store the remainder of the unprocessed
  185.  *              input data in encr->deferredData.
  186.  *              
  187.  *
  188.  * Note:
  189.  *
  190.  *              DES cipher-block chaining (CBC) is used, and the CBC information
  191.  *              is encoded in the desReadContext data structure.  Also note that
  192.  *              the history of the CBC consists of the entire communications
  193.  *              session between client and server up to this point in time.
  194.  */
  195.  
  196. static Int4
  197. DoDesRead(NI_EncrDataPtr encr, CharPtr buf, int bytesRead, int len)
  198. {
  199.     UcharPtr scratchInbuf;
  200.     UcharPtr ip;               /* ptr to next byte in scratchInbuf */
  201.     Int4 retval;
  202.     Int2 roomInBuffer;
  203.     Int2 avail;
  204.     Int2 bytesToDecrypt;
  205.     Int2 bytesToCopy;
  206.     UcharPtr scratchOutbuf;
  207.     Boolean done;
  208.  
  209.     if (bytesRead <= 0 || buf == NULL || encr == NULL)
  210.         return 0;
  211.  
  212.     if (encr->state == ENCR_DES_STATE_IDLE)
  213.     {
  214.         encr->numDeferredBytes = 0;
  215.         encr->realDataLeft = 0;
  216.         encr->bytesToRead = 0;
  217.     }
  218.  
  219.     if ((scratchInbuf = (UcharPtr) MemNew(bytesRead + encr->numDeferredBytes)) ==
  220.         NULL)
  221.         return 0;
  222.     ip = scratchInbuf;
  223.  
  224.     MemCpy ((CharPtr) scratchInbuf, (CharPtr) encr->deferredData,
  225.             encr->numDeferredBytes);
  226.     MemCpy ((CharPtr) &scratchInbuf[encr->numDeferredBytes], buf, bytesRead);
  227.     bytesRead += encr->numDeferredBytes;
  228.     encr->numDeferredBytes = 0;
  229.     retval = 0;
  230.     done = FALSE;
  231.  
  232.     while (bytesRead > 0 && ! done)
  233.     {
  234.         switch (encr->state) {
  235.         case ENCR_DES_STATE_IDLE:
  236.             if (bytesRead == 1)
  237.             {
  238.                 encr->realDataLeft = *ip++;
  239.                 encr->state = ENCR_DES_STATE_GOT1LENBYTE;
  240.                 done = TRUE;
  241.                 bytesRead--;
  242.             } else {
  243.                 encr->realDataLeft = ip[0] * 256 + ip[1];
  244.                 encr->bytesToRead = ((encr->realDataLeft + 7) / 8) * 8;
  245.                 encr->numDeferredBytes = 0;
  246.                 ip += 2;
  247.                 bytesRead -= 2;
  248.                 encr->state = ENCR_DES_STATE_INSTREAM;
  249.             }
  250.             break;
  251.         case ENCR_DES_STATE_GOT1LENBYTE:
  252.             encr->realDataLeft = encr->realDataLeft * 256 + *ip++;
  253.             encr->bytesToRead = ((encr->realDataLeft + 7) / 8) * 8;
  254.             encr->numDeferredBytes = 0;
  255.             bytesRead--;
  256.             encr->state = ENCR_DES_STATE_INSTREAM;
  257.             break;
  258.         case ENCR_DES_STATE_INSTREAM:
  259.             avail = MIN(encr->bytesToRead, bytesRead);
  260.             roomInBuffer = len - retval;
  261.             bytesToDecrypt = (MIN(roomInBuffer, avail) / 8) * 8;
  262.             if (bytesToDecrypt <= 0)
  263.             {
  264.                 if (roomInBuffer >= MAX(encr->realDataLeft, 0))
  265.                 { /* we can squeeze this in */
  266.                     bytesToDecrypt = 8;
  267.                 } else {
  268.                     done = TRUE;
  269.                     break;
  270.                 }
  271.             }
  272.             scratchOutbuf = (UcharPtr) MemNew(bytesToDecrypt);
  273.             DES_CBCUpdate ((DES_CBC_CTX PNTR) encr->desReadContext,
  274.                            scratchOutbuf, ip, bytesToDecrypt);
  275.             bytesToCopy = MIN(encr->realDataLeft, bytesToDecrypt);
  276.             MemCpy (buf, (CharPtr) scratchOutbuf, bytesToCopy);
  277.             MemFree (scratchOutbuf);
  278.             buf += bytesToCopy;
  279.             retval += bytesToCopy;
  280.             ip += bytesToDecrypt;
  281.             bytesRead -= bytesToDecrypt;
  282.             encr->bytesToRead -= bytesToDecrypt;
  283.             encr->realDataLeft -= bytesToDecrypt;
  284.             if (encr->bytesToRead <= 0)
  285.             {
  286.                 encr->state = ENCR_DES_STATE_IDLE;
  287.                 encr->numDeferredBytes = 0;
  288.                 encr->realDataLeft = 0;
  289.                 encr->bytesToRead = 0;
  290.             }
  291.             break;
  292.         }
  293.     }
  294.  
  295.     if (bytesRead > 0)
  296.     {
  297.         encr->numDeferredBytes = bytesRead;
  298.         if (bytesRead > sizeof(encr->deferredData))
  299.         {
  300.             ErrPostEx(SEV_ERROR, 0, 0, "Too much deferred decryption data %d bytes", bytesRead);
  301.         } else {
  302.             MemCpy ((CharPtr) encr->deferredData, (CharPtr) ip, bytesRead);
  303.         }
  304.     }
  305.  
  306.     MemFree (scratchInbuf);
  307.  
  308.     return retval;
  309. }
  310.  
  311.  
  312. /*
  313.  * Purpose:     DES read filter
  314.  *
  315.  * Parameters:
  316.  *   mhvoid     Pointer to message handle data structure
  317.  *   buf        Input buffer containing encrypted data, and output plaintext
  318.  *   bytesRead  Length of input data in buf
  319.  *   len        Max # of bytes which will fit in buf (requested read length)
  320.  *   extra_buf  unused, but required for NI_ReadFilt declaration
  321.  *   extra_buf_len  unused, but required for NI_ReadFilt declaration
  322.  *
  323.  * Returns:
  324.  *                The length of the resulting plaintext which has been processed
  325.  *
  326.  * Description:
  327.  *              Uses DoDesRead() to process input data, and returns to caller
  328.  */
  329.  
  330. static Int4
  331. DesReadFilt(VoidPtr mhvoid, CharPtr buf, int bytesRead, int len, CharPtr PNTR extra_buf, Int4Ptr extra_buf_len)
  332. {
  333.     NI_HandPtr mh = (NI_HandPtr) mhvoid;
  334.  
  335.     if (bytesRead <= 0 || buf == NULL || mh == NULL ||
  336.         mh->encryption == NULL)
  337.         return 0;
  338.     return DoDesRead(mh->encryption, buf, bytesRead, len);
  339. }
  340.  
  341.  
  342. /*
  343.  * Purpose:     Setup DES encryption for this message handle
  344.  *
  345.  * Parameters:
  346.  *   mh         Pointer to message handle data structure
  347.  *   desKey     DES key to be used for the life of this session
  348.  *
  349.  * Returns:
  350.  *                TRUE if setup was successful, FALSE otherwise
  351.  *
  352.  * Description:
  353.  *              Allocates an encryption data structure to attach to the message
  354.  *              handle, as well as the RSAREF data structures for both reading
  355.  *              and writing data.  Note that the read and write data structures
  356.  *              are each handling an independent half-duplex channel, i.e,
  357.  *              either client->server or server->client data.
  358.  *
  359.  * Note:
  360.  *              A caller which uses this function must also call
  361.  *              NI_DestroyEncrStruct() when it is time to destroy the message
  362.  *              handle.
  363.  */
  364.  
  365. Boolean LIBCALL
  366. NI_SetupDESEncryption(NI_HandPtr mh, UcharPtr desKey)
  367. {
  368.     Uchar iv[8];
  369.     NI_EncrDataPtr encr;
  370.  
  371.     if (mh->encryption != NULL)
  372.         return FALSE;
  373.     if ((encr = MemNew(sizeof(*encr))) == NULL)
  374.         return FALSE;
  375.     mh->encryption = encr;
  376.     encr->encrType = ENCR_DES_TYPE; /* the only possibility, for now */
  377.     encr->state = ENCR_DES_STATE_IDLE;
  378.     encr->write_filter = DesWriteFilt;
  379.     encr->read_filter = DesReadFilt;
  380.     encr->desWriteContext = (DES_CBC_CTX PNTR) MemNew(sizeof(DES_CBC_CTX));
  381.     MemSet((CharPtr) iv, '\0', sizeof(iv));
  382.     DES_CBCInit((DES_CBC_CTX PNTR) encr->desWriteContext, desKey, iv, TRUE);
  383.     encr->desReadContext = (DES_CBC_CTX PNTR) MemNew(sizeof(DES_CBC_CTX));
  384.     MemSet((CharPtr) iv, '\0', sizeof(iv));
  385.     DES_CBCInit((DES_CBC_CTX PNTR) encr->desReadContext, desKey, iv, FALSE);
  386.  
  387.     return TRUE;
  388. }
  389.  
  390.  
  391. /*
  392.  * Purpose:     Seed the random number generator if necessary
  393.  *
  394.  * Description:
  395.  *              If this function has not previously been called, it seeds the NCBI
  396.  *              random number generator with the best pseudo-random data available
  397.  *              on all systems, namely the current time in seconds, and a notion
  398.  *              of the application's process ID.  A high-granularity time is
  399.  *              also included when available.
  400.  *
  401.  * Note:
  402.  *              It would be helpful to have another NCBI function which returns
  403.  *              the highest-granularity time available, e.g., many systems have
  404.  *              microsecond and/or ticks available.  Any other creative data
  405.  *              available on this system (e.g., for UNIX systems, how many inodes
  406.  *              are in use in the root filesystem of this computer) would also be
  407.  *              helpful to help defeat malicious attempts to crack the security
  408.  *              of this encryption subsystem.
  409.  */
  410.  
  411. static void
  412. SetRandomSeed(void)
  413. {
  414.     static Boolean inited = FALSE;
  415.     Int4 highGranularity = 0;
  416. #ifdef OS_UNIX
  417.     struct timeval tv;
  418.  
  419.     gettimeofday(&tv, NULL);
  420.     highGranularity = tv.tv_usec;
  421. #endif
  422. #ifdef OS_MAC
  423.     highGranularity = clock();
  424. #endif
  425. #ifdef WIN_MSWIN
  426.     highGranularity = (Int4) GetCurrentTime();
  427. #endif
  428.     
  429.     if (! inited)
  430.     {
  431.         RandomSeed ((long) (GetSecs() | Nlm_GetAppProcessID() | highGranularity));
  432.         inited = TRUE;
  433.     }
  434. }
  435.  
  436.  
  437.  
  438. /*
  439.  * Purpose:     Initialize an RSAREF random data structure used for RSA encryption
  440.  *
  441.  * Parameters:
  442.  *     randomStruct   The data structure to be populated
  443.  *
  444.  * Description:
  445.  *              Seed the random number generator if necessary, create a random
  446.  *              R_RANDOM_STRUCT data structure, and populate it with
  447.  *              pseudo-random data.  When available, data from the NCBI config.
  448.  *              file is exclusive-ORed into each pseudo-random number, so
  449.  *              that an RSA key cannot subsequently be broken by trying all
  450.  *              2^^32 possible values of the random number generator.
  451.  */
  452.  
  453. static void InitRandomStruct (randomStruct)
  454. R_RANDOM_STRUCT *randomStruct;
  455. {
  456.     unsigned int bytesNeeded;
  457.     long ran;
  458.     Int4 seednum = 0;
  459.     Char buf[22];
  460.     Char moredata[10];
  461.  
  462.     SetRandomSeed();
  463.     R_RandomInit (randomStruct);
  464.  
  465.     while (1) {
  466.         R_GetRandomBytesNeeded (&bytesNeeded, randomStruct);
  467.         if (bytesNeeded == 0)
  468.             break;
  469.  
  470.         ran = RandomNum();
  471.         sprintf (buf, "CONFOUND_%d", seednum);
  472.         GetAppParam("NCBI", "NET_SERV", buf, "0", moredata, sizeof moredata);
  473.         ran ^= atoi(moredata);
  474.         seednum++;
  475.         R_RandomUpdate (randomStruct, (UcharPtr) &ran, MIN(bytesNeeded, sizeof ran));
  476.     }
  477. }
  478.  
  479.  
  480.  
  481. /*
  482.  * Purpose:     Convert pub encryption key from RSAREF format to internal NCBI fmt
  483.  *
  484.  * Parameters:
  485.  *     publicKey    Key in RSAREF format
  486.  *
  487.  * Returns:
  488.  *                  Key in NCBI format, or NULL if an error occurred
  489.  *
  490.  * Description:
  491.  *              Convert RSAREF public key data structure into a form which is 
  492.  *              suitable for being stored and transmitted in ASN.1
  493.  */
  494.  
  495. static NI_PubKeyPtr
  496. PubKeyToInternalFormat(R_RSA_PUBLIC_KEY PNTR publicKey)
  497. {
  498.     NI_PubKeyPtr retval = (NI_PubKeyPtr) MemNew(sizeof(*retval));
  499.  
  500.     if (retval == NULL || publicKey == NULL)
  501.         return NULL;
  502.     retval->bits = publicKey->bits;
  503.     retval->modulus = BSNew(sizeof(publicKey->modulus));
  504.     BSWrite(retval->modulus, (VoidPtr) publicKey->modulus, sizeof(publicKey->modulus));
  505.     retval->exponent = BSNew(sizeof(publicKey->exponent));
  506.     BSWrite(retval->exponent, (VoidPtr) publicKey->exponent, sizeof(publicKey->exponent));
  507.     return retval;
  508. }
  509.  
  510.  
  511.  
  512. /*
  513.  * Purpose:     Convert pub encryption key from internal NCBI format to RSAREF fmt
  514.  *
  515.  * Parameters:
  516.  *     internal     Key in NCBI format
  517.  *
  518.  * Returns:
  519.  *                  Key in RSAREF format, or NULL if an error occurred
  520.  *
  521.  * Description:
  522.  *              Produce RSAREF public key data structure from a form which is 
  523.  *              suitable for being stored and transmitted in ASN.1
  524.  */
  525.  
  526. static R_RSA_PUBLIC_KEY PNTR
  527. InternalToPubKeyFormat(NI_PubKeyPtr internal)
  528. {
  529.     R_RSA_PUBLIC_KEY PNTR retval;
  530.  
  531.     if (internal == NULL || internal->modulus == NULL ||
  532.         internal->exponent == NULL)
  533.         return NULL;
  534.     retval = (R_RSA_PUBLIC_KEY PNTR) MemNew(sizeof(*retval));
  535.     retval->bits = internal->bits;
  536.     BSSeek (internal->modulus, 0, SEEK_SET);
  537.     BSSeek (internal->exponent, 0, SEEK_SET);
  538.     BSRead(internal->modulus, retval->modulus, sizeof(retval->modulus));
  539.     BSRead(internal->exponent, retval->exponent, sizeof(retval->exponent));
  540.     return retval;
  541. }
  542.  
  543.  
  544. /*
  545.  * Purpose:     Compare two public encryption keys for equality
  546.  *
  547.  * Parameters:
  548.  *     x            Public key #1
  549.  *     y            Public key #2
  550.  *
  551.  * Returns:
  552.  *                  TRUE if keys match, FALSE otherwise
  553.  *
  554.  * Description:
  555.  *              Converts keys into RSAREF format, because these are flat data
  556.  *              structures and hence easy to compare. 
  557.  */
  558.  
  559. Boolean LIBCALL
  560. NI_PubKeysEqual(NI_PubKeyPtr x, NI_PubKeyPtr y)
  561. {
  562.     R_RSA_PUBLIC_KEY PNTR xRsa = InternalToPubKeyFormat(x);
  563.     R_RSA_PUBLIC_KEY PNTR yRsa = InternalToPubKeyFormat(y);
  564.     Boolean retval;
  565.  
  566.     if (xRsa == NULL && yRsa== NULL)
  567.         return TRUE;
  568.     if (xRsa == NULL || yRsa== NULL)
  569.     {
  570.         retval = FALSE;
  571.     } else {
  572.         retval = MemCmp((CharPtr) xRsa, (CharPtr) yRsa, sizeof (*xRsa)) == 0;
  573.     }
  574.     MemFree (xRsa);
  575.     MemFree (yRsa);
  576.     return retval;
  577. }
  578.  
  579.  
  580.  
  581. /*
  582.  * Purpose:     Pseudo-randomly generate an 8-byte DES key
  583.  *
  584.  * Parameters:
  585.  *     desKey       Resulting DES key
  586.  *
  587.  * Description:
  588.  *              Generates 8-byte DES key, grabbing 2 bytes from each of 4 pseudo-
  589.  *              randomly generated long integers.
  590.  *
  591.  * Note:
  592.  *              Only 2 bytes are used from the long integer, because it's
  593.  *              conceivable that a machine exists without 4-byte long integers.
  594.  *              In hindsight, sizeof(long) could be used to determine how much to
  595.  *              use, but this is a reasonable implementation.
  596.  */
  597.  
  598. void
  599. NI_GenerateDESKey(UcharPtr desKey)
  600. {
  601.     Int2 i;
  602.     long ran;
  603.  
  604.     SetRandomSeed();
  605.     for (i = 0; i < 8;)
  606.     {
  607.         ran = RandomNum();
  608.         desKey[i++] = ran & 255;
  609.         desKey[i++] = (ran >> 8) & 255;
  610.     }
  611. }
  612.  
  613.  
  614.  
  615. /*
  616.  * Purpose:     Generate pub encryption public + private keys, and write to files
  617.  *
  618.  * Parameters:
  619.  *     bits        Length of key modulus in bits, within a restricted range
  620.  *     pubAip      AsnIoPtr to where public-key should be stored
  621.  *     privFp      File pointer to where private key should be stored 
  622.  *
  623.  * Returns:
  624.  *              TRUE if operations were successful, FALSE otherwise
  625.  *
  626.  * Description:
  627.  *              Generates public and private keys using RSAREF function, and then
  628.  *              output to AsnIoPtr and file pointer.
  629.  *
  630.  * Note:
  631.  *              An AsnIoPtr is used for the public key, because the public key
  632.  *              must be transmittable in a canonical format.  Since private keys
  633.  *              are never transmitted, a single block of memory is used for
  634.  *              private keys, and private keys are stored in a single chunk on
  635.  *              disk.  This reduces the number of special data structures and
  636.  *              ASN.1 object loaders which needed to be constructed to add
  637.  *              encryption to NCBI Network Services.
  638.  */
  639.  
  640. Boolean LIBCALL
  641. NI_GenAndWritePEMKeys(Int2 bits, AsnIoPtr pubAip, FILE *privFp)
  642. {
  643.     R_RSA_PUBLIC_KEY publicKey;
  644.     R_RSA_PRIVATE_KEY privateKey;
  645.     R_RSA_PROTO_KEY protoKey;
  646.     R_RANDOM_STRUCT randomStruct;
  647.     int retval;
  648.     AsnModulePtr amp;
  649.     AsnTypePtr pubAtp;
  650.     NI_PubKeyPtr internalPub;
  651.     NI_HandPtr dummyHand;
  652.  
  653.     if (bits < MIN_RSA_MODULUS_BITS || bits > MAX_RSA_MODULUS_BITS)
  654.         return FALSE;
  655.     if (pubAip == NULL || privFp == NULL)
  656.         return FALSE;
  657.     /* create a dummy message handle to ensure that ASN.1 is loaded */
  658.     dummyHand = (NI_HandPtr) MsgMakeHandle(FALSE);
  659.     MsgDestroyHandle(dummyHand);
  660.     if ((amp = AsnAllModPtr()) == NULL)
  661.         return FALSE;
  662.     if ((pubAtp = AsnTypeFind(amp, "RSA-Pubkey")) == NULL)
  663.         return FALSE;
  664.     InitRandomStruct (&randomStruct);
  665.     protoKey.bits = bits;
  666.     protoKey.useFermat4 = 1;
  667.     retval = R_GeneratePEMKeys(&publicKey, &privateKey, &protoKey, &randomStruct);
  668.     if (retval != 0)
  669.     {
  670.         ErrPostEx(SEV_ERROR, 0, 0, "Error when generating PEM keys %d", retval);
  671.     }
  672.     internalPub = PubKeyToInternalFormat(&publicKey);
  673.     NI_WritePubKey(pubAip, pubAtp, internalPub);
  674.  
  675.     FileWrite(&privateKey, sizeof(privateKey), 1, privFp);
  676.  
  677.     return TRUE;
  678. }
  679.  
  680.  
  681.  
  682. /*
  683.  * Purpose:     Write a public key to a standard location on client machines
  684.  *
  685.  * Parameters:
  686.  *
  687.  *   pub           The public-key to be written to standard file
  688.  *
  689.  * Returns:
  690.  *              TRUE if operations were successful, FALSE otherwise
  691.  *
  692.  * Description:
  693.  *              Writes public-key to a file in the DATA directory.
  694.  *              Alternatively, the public-key could be stored anywhere ...
  695.  *              originally it was stored in the NCBI configuration file. 
  696.  */
  697.  
  698. Boolean LIBCALL
  699. NI_WritePubKeyToConfig (NI_PubKeyPtr pub)
  700. {
  701.     Char fname[PATH_MAX];
  702.     AsnIoPtr aip;
  703.     AsnModulePtr amp;
  704.     AsnTypePtr pubAtp;
  705.     Boolean retval;
  706.     NI_HandPtr dummyHand;
  707.  
  708.     if (pub == NULL)
  709.     {
  710.         return FALSE;
  711.     }
  712.  
  713.     /* create a dummy message handle to ensure that ASN.1 is loaded */
  714.     dummyHand = (NI_HandPtr) MsgMakeHandle(FALSE);
  715.     MsgDestroyHandle(dummyHand);
  716.     if ((amp = AsnAllModPtr()) == NULL)
  717.         return FALSE;
  718.     if ((pubAtp = AsnTypeFind(amp, "RSA-Pubkey")) == NULL)
  719.         return FALSE;
  720.  
  721.     if (! FindPath("ncbi", "ncbi", "data", fname, sizeof (fname)))
  722.     {
  723.         ErrPost(CTX_NCBIOBJ, 1, "FindPath failed");
  724.         return FALSE;
  725.     }
  726.  
  727.     StringCat(fname, "pubkey.enc");
  728.  
  729.     if ((aip = AsnIoOpen(fname, "w")) == NULL)
  730.     {
  731.         return FALSE;
  732.     }
  733.     NI_WritePubKey(aip, pubAtp, pub);
  734.     AsnIoClose(aip);
  735.     return TRUE;
  736. }
  737.  
  738. /*
  739.  * Purpose:     Read a public key from a standard configuration location
  740.  *
  741.  * Returns:
  742.  *              A pointer to the allocated public-key, or NULL if failed
  743.  *
  744.  * Description:
  745.  *              Reads public-key from a file in the DATA directory.
  746.  *              Alternatively, the public-key could be stored anywhere ...
  747.  *              originally it was stored in the NCBI configuration file. 
  748.  */
  749.  
  750. NI_PubKeyPtr LIBCALL
  751. NI_ReadPubKeyFromConfig (void)
  752. {
  753.     Char fname[PATH_MAX];
  754.     AsnIoPtr aip;
  755.     NI_PubKeyPtr pub;
  756.     FILE *fp;
  757.  
  758.     if (! FindPath("ncbi", "ncbi", "data", fname, sizeof (fname)))
  759.     {
  760.         ErrPost(CTX_NCBIOBJ, 1, "FindPath failed");
  761.         return NULL;
  762.     }
  763.  
  764.     StringCat(fname, "pubkey.enc");
  765.     /* try opening the file first, to suppress AsnIoOpen messages */
  766.     if ((fp = FileOpen(fname, "r")) == NULL)
  767.     {
  768.         return NULL;
  769.     } else {
  770.         FileClose(fp);
  771.     }
  772.     if ((aip = AsnIoOpen(fname, "r")) == NULL)
  773.     {
  774.         FileRemove(fname);
  775.         return NULL;
  776.     }
  777.     pub = (NI_PubKeyPtr) NI_MakePubKey();
  778.     if (NI_ReadPubKey(aip, NULL, pub) < 0)
  779.     {
  780.         NI_DestroyPubKey(pub);
  781.         pub = NULL;
  782.     }
  783.     AsnIoClose(aip);
  784.  
  785.     return pub;
  786. }
  787.  
  788. /*
  789.  * Purpose:     Make a copy of a public key
  790.  *
  791.  * Parameters:
  792.  *   orig          The key to be copied
  793.  *
  794.  * Returns:
  795.  *              A pointer to the copy of the public-key, or NULL if failed
  796.  *
  797.  * Description:
  798.  *              Makes a copy of a public key
  799.  */
  800.  
  801. NI_PubKeyPtr LIBCALL
  802. NI_PubKeyDup (NI_PubKeyPtr orig)
  803. {
  804.     NI_PubKeyPtr dup;
  805.  
  806.     if (orig == NULL)
  807.         return NULL;
  808.     dup = MemNew(sizeof(*dup));
  809.     dup->bits = orig->bits;
  810.     dup->modulus = BSDup(orig->modulus);
  811.     dup->exponent = BSDup(orig->exponent);
  812.     return dup;
  813. }
  814.     
  815.  
  816. /*
  817.  * Purpose:     Load a private-key from the specified file pointer
  818.  *
  819.  * Parameters:
  820.  *   fp            File pointer from which to read private key
  821.  *   privKeyLenPtr Pointer to where the length of private key may be stored
  822.  *
  823.  * Returns:
  824.  *              A pointer to the resulting data structure, or NULL if unsuccessful
  825.  *
  826.  * Description:
  827.  *              Reads private key from data file.
  828.  */
  829.  
  830. VoidPtr LIBCALL
  831. NI_LoadPrivKey(FILE *fp, Int2Ptr privKeyLenPtr)
  832. {
  833.     R_RSA_PRIVATE_KEY PNTR privKey;
  834.  
  835.     privKey = (R_RSA_PRIVATE_KEY PNTR) MemNew(sizeof(*privKey));
  836.  
  837.     FileRead(privKey, sizeof(*privKey), 1, fp);
  838.  
  839.     if (privKeyLenPtr != NULL)
  840.     {
  841.         *privKeyLenPtr = sizeof(*privKey);
  842.     }
  843.  
  844.     return privKey;
  845. }
  846.  
  847. /*
  848.  * Purpose:     Perform public-key decryption
  849.  *
  850.  * Parameters:
  851.  *   pKey          Private key
  852.  *   plainText     Pointer to resulting plaintext
  853.  *   cipherText    Ciphertext to be decrypted
  854.  *   cipherTextLen Length of cipherText
  855.  *
  856.  * Returns:
  857.  *              The length of resulting plaintext, or a negative error code
  858.  *
  859.  * Description:
  860.  *              Decrypts the specified ciphertext using the specified private
  861.  *              key.  Subsequently resizes the resulting plaintext to be only
  862.  *              as large as is needed.
  863.  *
  864.  * Note:
  865.  *              The caller must free the pointer to the resulting plaintext.
  866.  */
  867.  
  868. Int2 LIBCALL
  869. NI_PubKeyDecrypt(VoidPtr pKey, UcharPtr PNTR plainText, UcharPtr cipherText, Int2 cipherTextLen)
  870. {
  871.     R_RSA_PRIVATE_KEY PNTR privKey = (R_RSA_PRIVATE_KEY PNTR) pKey;
  872.     int plainTextLen;
  873.     UcharPtr pText1, pText2;
  874.  
  875.     if (pKey == NULL || plainText == NULL || cipherText == NULL)
  876.         return -1;
  877.  
  878.     *plainText = NULL;
  879.     /* plain text is certainly shorter than ciphertext */
  880.     pText1 = (UcharPtr) MemNew(cipherTextLen);
  881.  
  882.     if (RSAPrivateDecrypt(pText1, &plainTextLen, cipherText, cipherTextLen, privKey) != 0)
  883.     {
  884.         MemFree (pText1);
  885.         return -2;
  886.     }
  887.     pText2 = (UcharPtr) MemDup(pText1, plainTextLen);
  888.     MemFree (pText1);
  889.     *plainText = pText2;
  890.     return ((Int2) plainTextLen);
  891. }
  892.  
  893.  
  894. /*
  895.  * Purpose:     Perform public-key encryption
  896.  *
  897.  * Parameters:
  898.  *   pub           Public key
  899.  *   plainText     Plaintext to be encrypted
  900.  *   plainTextLen  Length of plainText
  901.  *   cipherText    Pointer to resulting ciphertext
  902.  *
  903.  * Returns:
  904.  *              The length of resulting ciphertext, or a negative error code
  905.  *
  906.  * Description:
  907.  *              Encrypts the specified plaintext using the specified public
  908.  *              key.  Subsequently resizes the resulting ciphertext to be only
  909.  *              as large as is needed.
  910.  *
  911.  * Note:
  912.  *              The caller must free the pointer to the resulting ciphertext.
  913.  */
  914.  
  915. Int2 LIBCALL
  916. NI_PubKeyEncrypt(NI_PubKeyPtr pub, UcharPtr plainText, Int2 plainTextLen, UcharPtr PNTR cipherText)
  917. {
  918.     R_RSA_PUBLIC_KEY PNTR pubKeyPtr;
  919.     R_RANDOM_STRUCT randomStruct;
  920.     UcharPtr cipher, cipher2;
  921.     int cipherTextLen = 0;
  922.  
  923.     if (pub == NULL || plainText == NULL || cipherText == NULL)
  924.     {
  925.         return -1;
  926.     }
  927.     *cipherText = NULL;
  928.     if ((pubKeyPtr = InternalToPubKeyFormat(pub)) == NULL)
  929.     {
  930.         return -2;
  931.     }
  932.     cipher = (UcharPtr) MemNew(plainTextLen * 2 + 64);
  933.     InitRandomStruct (&randomStruct);
  934.     if (RSAPublicEncrypt(cipher, &cipherTextLen, plainText, plainTextLen, pubKeyPtr, &randomStruct) != 0)
  935.     {
  936.         MemFree (cipher);
  937.         MemFree (pubKeyPtr);
  938.         return -3;
  939.     }
  940.     MemFree (pubKeyPtr);
  941.     /* reduce to the proper length */
  942.     cipher2 = (UcharPtr) MemDup(cipher, cipherTextLen);
  943.     MemFree (cipher);
  944.     *cipherText = cipher2;
  945.     return ((Int2) cipherTextLen);
  946. }
  947.  
  948. /*
  949.  * Purpose:     Free an encryption data structure, and erase secret data
  950.  *
  951.  * Parameters:
  952.  *   encr          Encryption data structure to be destroyed
  953.  *
  954.  *
  955.  * Description:
  956.  *              Frees an encryption data structure, and erases secret data
  957.  *              which could be used by a hostile party to break the encryption.
  958.  *              The caller must free the pointer to the resulting ciphertext.
  959.  */
  960.  
  961. void
  962. NI_DestroyEncrStruct (NI_EncrDataPtr encr)
  963. {
  964.     if (encr == NULL)
  965.         return;
  966.     if (encr->desReadContext != NULL)
  967.     {
  968.         /* clear this memory for security reasons */
  969.         MemSet(encr->desReadContext, '\0', sizeof(DES_CBC_CTX));
  970.         MemFree(encr->desReadContext);
  971.     }
  972.     if (encr->desWriteContext != NULL)
  973.     {
  974.         /* clear this memory for security reasons */
  975.         MemSet(encr->desWriteContext, '\0', sizeof(DES_CBC_CTX));
  976.         MemFree(encr->desWriteContext);
  977.     }
  978.     MemFree(encr);
  979. }
  980.  
  981. /*
  982.  * Purpose:     Indicates whether encryption is available
  983.  *
  984.  * Returns:     Always TRUE for this file, always FALSE for stub file
  985.  *
  986.  *
  987.  * Description:
  988.  *              Indicates to the caller whether encryption is available, and
  989.  *              whether or not it is safe to call the other encryption
  990.  *              functions.
  991.  */
  992.  
  993. Boolean LIBCALL
  994. NI_EncrAvailable(void)
  995. {
  996.     return TRUE;
  997. }
  998.